﻿using Common.Exceptions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;

namespace Common.Cache
{
    /// <summary>
    /// 多级缓存
    /// </summary>
    public sealed class MultiLevelCache : ICache
    {
        private static ICache Redis;
        private readonly static ICache memoryCache = new MemoryCache();
        private static MultiLevelCache multiCache;
        private static readonly object lockObj = new object();
        private static readonly ConcurrentDictionary<string, DateTime> keeps = new ConcurrentDictionary<string, DateTime>();
        private MultiLevelCache() { }
        /// <summary>
        /// 多级缓存实例
        /// </summary>
        public static MultiLevelCache Default
        {
            get
            {
                if (multiCache == null)
                {
                    lock (lockObj)
                    {
                        if (multiCache == null)
                        {
                            if (FreeRedisCache.ConfigRedis == null)
                            {
                                FreeRedisCache.ConfigRedis = ConfigureRedis;
                            }
                            Redis = FreeRedisCache.Instance;
                            multiCache = new MultiLevelCache();
                        }
                    }
                }
                return multiCache;
            }
        }
        /// <summary>
        /// 配置redis
        /// </summary>
        public static Func<string, string> ConfigureRedis;
        private bool NeedRefresh(string key)
        {
            keeps.TryGetValue(key, out DateTime addTime);
            if ((DateTime.Now - addTime).TotalSeconds < 120)
            {
                return false;
            }
            var rVal = Redis.GetString(key);
            return string.IsNullOrEmpty(rVal);
        }
        /// <inheritdoc/>
        public bool Add(string key, object value, TimeSpan? expiry = null)
        {
            var now = DateTime.Now;
            keeps.AddOrUpdate(key, now, (k, v) => now);
            return memoryCache.Add(key, value, expiry) && Redis.Add(key, now.ToString("yyyy-MM-dd HH:mm:ss"));
        }
        /// <inheritdoc/>
        public object GetObject(string key)
        {
            return NeedRefresh(key) ? null : memoryCache.GetObject(key);
        }
        /// <inheritdoc/>
        public T Get<T>(string key)
        {
            return NeedRefresh(key) ? default : memoryCache.Get<T>(key);
        }
        /// <inheritdoc/>
        [Obsolete("this method does not work properly, and will be deleted some day in the future!")]
        public List<T> GetList<T>(string pattern)
        {
            throw new NotSupportedException();
        }
        /// <inheritdoc/>
        public bool Delete(string key)
        {
            keeps.TryRemove(key, out _);
            return Redis.Delete(key) && memoryCache.Delete(key);
        }
        /// <inheritdoc/>
        public bool Exist(string key)
        {
            return memoryCache.Exist(key);
        }
        /// <inheritdoc/>
        public T GetOrAdd<T>(string key, Func<string, T> func, TimeSpan? expiry = null)
        {
            var old = this.Get<T>(key);
            if (NeedRefresh(key) || old == null)
            {
                var val = func(key);
                this.Add(key, val, expiry);
                return val;
            }
            return (T)old;
        }
        /// <inheritdoc/>
        public T GetOrAdd<T>(string key, Func<string, JCacheEntry<T>> func)
        {
            var old = this.Get<T>(key);
            if (NeedRefresh(key) || old == null)
            {
                var entry = func(key);
                
                if (entry != null)
                {
                    old = entry.Data;
                    this.Add(key, entry.Data, entry.Expiry);
                }
            }
            return old;
        }
        /// <inheritdoc/>
        public string GetString(string key)
        {
            return NeedRefresh(key) ? null : memoryCache.GetString(key);
        }
        /// <inheritdoc/>
        public bool Refresh(string key, object value = null, TimeSpan? expiry = null)
        {
            this.Delete(key);
            if (value != null)
            {
                this.Add(key, value, expiry);
            }
            return true;
        }
        /// <inheritdoc/>
        public string[] GetKeys(string pattern)
        {
            return memoryCache.GetKeys(pattern);
        }
    }
}
